// main.rs
use actix_web::{web, App, HttpResponse, HttpServer, Responder};
use chrono::prelude::*;
use serde::{Deserialize, Serialize};
use sha2::{Digest, Sha256};
use std::collections::{HashSet, HashMap};
use std::sync::Mutex;
use url::Url;
use reqwest::Client;
use futures::executor;
use std::fs;
use std::io::{self, Write};
use std::path::Path;
use actix_cors::Cors;

#[derive(Debug, Clone, Serialize, Deserialize)]
struct Transaction {
    sender: String,
    recipient: String,
    amount: u32,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
struct Block {
    index: u32,
    timestamp: String,
    transactions: Vec<Transaction>,
    previous_hash: String,
    nonce: u64,
    hash: String,
}

impl Block {
    fn calculate_hash(&self) -> String {
        let tx_data: String = self.transactions.iter()
            .map(|tx| format!("{}{}{}", tx.sender, tx.recipient, tx.amount))
            .collect();

        let data = format!(
            "{}{}{}{}{}",
            self.index, self.timestamp, tx_data, self.previous_hash, self.nonce
        );

        let mut hasher = Sha256::new();
        hasher.update(data);
        format!("{:x}", hasher.finalize())
    }
}

fn proof_of_work(mut block: Block, difficulty: usize) -> Block {
    let prefix = "0".repeat(difficulty);
    while !block.calculate_hash().starts_with(&prefix) {
        block.nonce += 1;
    }
    block.hash = block.calculate_hash();
    block
}

#[derive(Debug, Serialize, Deserialize, Clone)]
struct Wallet {
    address: String,
    balance: u32,
}

#[derive(Debug, Serialize, Deserialize)]
struct BlockchainData {
    chain: Vec<Block>,
    wallets: HashMap<String, u32>,
}

#[derive(Debug)]
struct Blockchain {
    chain: Vec<Block>,
    current_transactions: Vec<Transaction>,
    difficulty: usize,
    nodes: HashSet<String>,
    self_address: String,
    wallets: HashMap<String, u32>,
}

impl Blockchain {
    fn new(self_address: String) -> Self {
        // 尝试从文件加载区块链
        let loaded_data = Blockchain::load_from_file();
        
        let mut blockchain = match loaded_data {
            Ok(data) => {
                println!("💾 从文件加载了区块链数据");
                Blockchain {
                    chain: data.chain,
                    current_transactions: vec![],
                    difficulty: 4,
                    nodes: HashSet::new(),
                    self_address,
                    wallets: data.wallets,
                }
            },
            Err(_) => {
                println!("🆕 创建新的区块链");
                let mut bc = Blockchain {
                    chain: vec![],
                    current_transactions: vec![],
                    difficulty: 4,
                    nodes: HashSet::new(),
                    self_address,
                    wallets: HashMap::new(),
                };
                bc.new_block("0".to_string());
                bc
            }
        };
        
        // 确保系统钱包存在（用于挖矿奖励）
        if !blockchain.wallet_exists("system") {
            blockchain.create_wallet("system".to_string(), 1000000); // 系统钱包有足够代币发放奖励
        }
        
        blockchain
    }

    fn wallet_exists(&self, address: &str) -> bool {
        self.wallets.contains_key(address)
    }

    fn create_wallet(&mut self, address: String, initial_balance: u32) -> bool {
        if self.wallet_exists(&address) {
            return false;
        }
        
        self.wallets.insert(address.clone(), initial_balance);
        println!("👛 创建钱包：{} 初始余额：{}", address, initial_balance);
        true
    }

    fn get_balance(&self, address: &str) -> u32 {
        *self.wallets.get(address).unwrap_or(&0)
    }

    fn update_balance(&mut self, address: &str, amount: i64) -> bool {
        let current = self.get_balance(address) as i64;
        let new_balance = current + amount;
        
        if new_balance < 0 {
            return false;
        }
        
        self.wallets.insert(address.to_string(), new_balance as u32);
        true
    }

    fn new_transaction(&mut self, tx: Transaction) -> bool {
        // 验证交易
        if tx.sender != "system" {  // 系统产生的交易不需要验证余额
            let sender_balance = self.get_balance(&tx.sender);
            if sender_balance < tx.amount {
                println!("❌ 交易失败：{} 的余额不足", tx.sender);
                return false;
            }
            
            // 更新钱包余额
            self.update_balance(&tx.sender, -(tx.amount as i64));
        }
        
        // 如果接收者钱包不存在，创建一个
        if !self.wallet_exists(&tx.recipient) {
            self.create_wallet(tx.recipient.clone(), 0);
        }
        
        // 增加接收者余额
        self.update_balance(&tx.recipient, tx.amount as i64);
        
        println!("📨 新交易：{} → {}: {}", tx.sender, tx.recipient, tx.amount);
        self.current_transactions.push(tx);
        
        // 保存更新后的区块链状态
        let _ = self.save_to_file();
        
        true
    }

    fn new_block(&mut self, previous_hash: String) -> &Block {
        // 添加挖矿奖励交易
        let reward = Transaction {
            sender: "system".to_string(),
            recipient: "miner".to_string(), // 这里可以改为实际的矿工地址
            amount: 10, // 挖矿奖励
        };
        let _ = self.new_transaction(reward);
        
        let index = self.chain.len() as u32;
        let timestamp = Utc::now().to_rfc3339();
        let transactions = self.current_transactions.clone();

        let block = Block {
            index,
            timestamp,
            transactions,
            previous_hash,
            nonce: 0,
            hash: String::new(),
        };

        let mined_block = proof_of_work(block, self.difficulty);
        println!("⛏️ 区块已挖出：index={}, hash={}", mined_block.index, mined_block.hash);
        self.chain.push(mined_block);
        self.current_transactions.clear();
        
        // 保存更新后的区块链状态
        let _ = self.save_to_file();
        
        self.chain.last().unwrap()
    }

    fn last_block(&self) -> &Block {
        self.chain.last().unwrap()
    }

    // fn register_node(&mut self, address: &str) -> bool {
    //     if let Ok(parsed) = Url::parse(address) {
    //         return self.nodes.insert(parsed.origin().ascii_serialization());
    //     }
    //     false
    // }

    async fn resolve_conflicts(&mut self) -> bool {
        let client = reqwest::Client::new();
        let mut max_length = self.chain.len();
        let mut new_chain: Option<Vec<Block>> = None;
        let mut new_wallets: Option<HashMap<String, u32>> = None;
    
        for node in &self.nodes {
            let url = format!("{}/chain", node);
            println!("🔄 正在尝试从 {} 获取链", url);
            if let Ok(res) = client.get(&url).send().await {
                if let Ok(blockchain_data) = res.json::<BlockchainData>().await {
                    if blockchain_data.chain.len() > max_length {
                        max_length = blockchain_data.chain.len();
                        new_chain = Some(blockchain_data.chain);
                        new_wallets = Some(blockchain_data.wallets);
                    }
                }
            }
        }
    
        if let Some(chain) = new_chain {
            println!("✅ 替换本地链为长度为 {} 的远程链", max_length);
            self.chain = chain.clone();
            
            // 更新钱包状态
            if let Some(wallets) = new_wallets {
                self.wallets = wallets;
            }
    
            // 保存更新后的区块链状态
            let _ = self.save_to_file();
    
            // 广播新链的最后一个区块
            if let Some(last_block) = chain.last() {
                let peers = self.nodes.clone();
                let block_to_send = last_block.clone();
    
                for node in peers {
                    let url = format!("{}/blocks/receive", node);
                    let client = client.clone();
                    let block = block_to_send.clone();
                    tokio::spawn(async move {
                        match client.post(&url).json(&block).send().await {
                            Ok(res) => println!("📣 广播更新区块至 {}，响应: {}", node, res.status()),
                            Err(err) => eprintln!("❌ 广播失败: {}", err),
                        }
                    });
                }
            }
    
            true
        } else {
            println!("🟢 当前链已是最长");
            false
        }
    }

    // 保存区块链到文件
    fn save_to_file(&self) -> io::Result<()> {
        let blockchain_data = BlockchainData {
            chain: self.chain.clone(),
            wallets: self.wallets.clone(),
        };
        
        let json = serde_json::to_string(&blockchain_data)?;
        let mut file = fs::File::create("blockchain_data.json")?;
        file.write_all(json.as_bytes())?;
        println!("💾 区块链数据已保存到文件");
        Ok(())
    }
    
    // 从文件加载区块链
    fn load_from_file() -> io::Result<BlockchainData> {
        if !Path::new("blockchain_data.json").exists() {
            return Err(io::Error::new(io::ErrorKind::NotFound, "区块链数据文件不存在"));
        }
        
        let file_content = fs::read_to_string("blockchain_data.json")?;
        let blockchain_data: BlockchainData = serde_json::from_str(&file_content)?;
        Ok(blockchain_data)
    }
}

#[derive(Debug, Deserialize)]
struct TransactionPayload {
    sender: String,
    recipient: String,
    amount: u32,
}

#[derive(Debug, Deserialize, Serialize)]
struct RegisterNodes {
    nodes: Vec<String>,
}

#[derive(Debug, Deserialize)]
struct CreateWalletPayload {
    address: String,
    initial_balance: Option<u32>,
}

async fn create_wallet(
    blockchain: web::Data<Mutex<Blockchain>>,
    payload: web::Json<CreateWalletPayload>,
) -> impl Responder {
    let mut chain = blockchain.lock().unwrap();
    let initial_balance = payload.initial_balance.unwrap_or(0);
    
    if chain.create_wallet(payload.address.clone(), initial_balance) {
        let _ = chain.save_to_file();
        HttpResponse::Ok().json(format!("✅ 钱包已创建：{}", payload.address))
    } else {
        HttpResponse::BadRequest().json(format!("❌ 钱包已存在：{}", payload.address))
    }
}

async fn get_balance(
    blockchain: web::Data<Mutex<Blockchain>>,
    address: web::Path<String>,
) -> impl Responder {
    let chain = blockchain.lock().unwrap();
    let balance = chain.get_balance(&address);
    
    HttpResponse::Ok().json(format!("💰 余额：{}", balance))
}

async fn add_transaction(
    blockchain: web::Data<Mutex<Blockchain>>,
    payload: web::Json<TransactionPayload>,
) -> impl Responder {
    let tx = Transaction {
        sender: payload.sender.clone(),
        recipient: payload.recipient.clone(),
        amount: payload.amount,
    };

    let mut chain = blockchain.lock().unwrap();
    if chain.new_transaction(tx) {
        HttpResponse::Ok().json("✅ 交易已添加")
    } else {
        HttpResponse::BadRequest().json("❌ 交易失败")
    }
}

async fn mine_block(
    blockchain: web::Data<Mutex<Blockchain>>,
    miner_address: web::Path<String>,
) -> impl Responder {
    let mut chain = blockchain.lock().unwrap();
    
    // 确保矿工钱包存在
    if !chain.wallet_exists(&miner_address) {
        chain.create_wallet(miner_address.to_string(), 0);
    }
    
    // 添加挖矿奖励交易
    let reward_tx = Transaction {
        sender: "system".to_string(),
        recipient: miner_address.to_string(),
        amount: 10, // 挖矿奖励
    };
    chain.new_transaction(reward_tx);
    
    let prev_hash = chain.last_block().hash.clone();
    let new_block = chain.new_block(prev_hash).clone();
    let peers = chain.nodes.clone();
    drop(chain);

    let client = reqwest::Client::new();
    for node in peers {
        let url = format!("{}/blocks/receive", node);
        let block_to_send = new_block.clone();
        match client.post(&url).json(&block_to_send).send().await {
            Ok(res) => println!("✅ 已广播区块至 {node}，响应状态: {}", res.status()),
            Err(e) => eprintln!("❌ 广播至 {node} 失败: {}", e),
        }
    }

    HttpResponse::Ok().json(new_block)
}

async fn receive_block(
    blockchain: web::Data<Mutex<Blockchain>>,
    block: web::Json<Block>,
) -> impl Responder {
    let mut chain = blockchain.lock().unwrap();
    let last_block = chain.last_block().clone();

    if block.previous_hash == last_block.hash && block.index == last_block.index + 1 {
        if block.hash == block.calculate_hash() {
            println!("📥 接收到新区块并成功加入链中：index = {}", block.index);
            chain.chain.push(block.into_inner());
            let _ = chain.save_to_file();
            return HttpResponse::Ok().json("✅ 已添加新区块");
        }
    }

    let blockchain = blockchain.clone();
    tokio::task::spawn_blocking(move || {
        let mut chain = blockchain.lock().unwrap();
        let _ = executor::block_on(chain.resolve_conflicts());
    });

    HttpResponse::Accepted().json("⚠️ 区块未接上，已触发链同步")
}

async fn get_chain(blockchain: web::Data<Mutex<Blockchain>>) -> impl Responder {
    let chain = blockchain.lock().unwrap();
    let blockchain_data = BlockchainData {
        chain: chain.chain.clone(),
        wallets: chain.wallets.clone(),
    };
    HttpResponse::Ok().json(&blockchain_data)
}

async fn get_wallets(blockchain: web::Data<Mutex<Blockchain>>) -> impl Responder {
    let chain = blockchain.lock().unwrap();
    let mut filtered_wallets = chain.wallets.clone();
    filtered_wallets.remove("system");
    filtered_wallets.remove("miner");
    HttpResponse::Ok().json(&filtered_wallets)
}

async fn register_nodes(
    blockchain: web::Data<Mutex<Blockchain>>,
    payload: web::Json<RegisterNodes>,
) -> impl Responder {
    let mut chain = blockchain.lock().unwrap();

    for node in &payload.nodes {
        println!("🔗 注册节点: {}", node);
        if let Ok(parsed) = Url::parse(node) {
            let origin = parsed.origin().ascii_serialization();
            let is_new = chain.nodes.insert(origin.clone());

            if is_new {
                let reverse_payload = RegisterNodes {
                    nodes: vec![chain.self_address.clone()],
                };
                let url = format!("{}/nodes/register", node);

                tokio::spawn(async move {
                    let client = Client::new();
                    let res = client
                        .post(&url)
                        .json(&reverse_payload)
                        .timeout(std::time::Duration::from_secs(3))
                        .send()
                        .await;

                    match res {
                        Ok(resp) => println!("↩️ 反向注册响应: {}", resp.status()),
                        Err(e) => eprintln!("❌ 反向注册失败: {}", e),
                    }
                });
            }
        }
    }

    println!("📌 当前注册节点: {:?}", chain.nodes);
    drop(chain);

    let blockchain = blockchain.clone();
    tokio::task::spawn_blocking(move || {
        let mut chain = blockchain.lock().unwrap();
        let _ = executor::block_on(chain.resolve_conflicts());
    });
    
    HttpResponse::Ok().json("✅ 节点已注册（包含双向注册 + 链同步）")
}

async fn resolve_chain(blockchain: web::Data<Mutex<Blockchain>>) -> impl Responder {
    let blockchain = blockchain.clone();
    let replaced = tokio::task::spawn_blocking(move || {
        let mut chain = blockchain.lock().unwrap();
        executor::block_on(chain.resolve_conflicts())
    })
    .await
    .unwrap_or(false);

    if replaced {
        HttpResponse::Ok().json("✅ 本节点链已被替换为最长链")
    } else {
        HttpResponse::Ok().json("✅ 当前链是最长的，无需替换")
    }
}

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    let self_address = std::env::var("SELF_ADDRESS").unwrap_or_else(|_| "http://0.0.0.0:8888".to_string());
    let bind_addr = self_address.strip_prefix("http://").unwrap_or("0.0.0.0:8888");

    let blockchain = web::Data::new(Mutex::new(Blockchain::new(self_address.clone())));
    println!("🌐 区块链节点启动中: {}", self_address);

    HttpServer::new(move || {
        App::new()
            .wrap(
                Cors::default()
                    .allow_any_origin()
                    .allow_any_method()
                    .allow_any_header()
            )
            .app_data(blockchain.clone())
            .route("/transactions/new", web::post().to(add_transaction))
            .route("/mine/{miner_address}", web::get().to(mine_block))
            .route("/chain", web::get().to(get_chain))
            .route("/wallets", web::get().to(get_wallets))
            .route("/wallets/new", web::post().to(create_wallet))
            .route("/wallets/{address}/balance", web::get().to(get_balance))
            .route("/nodes/register", web::post().to(register_nodes))
            .route("/nodes/resolve", web::get().to(resolve_chain))
            .route("/blocks/receive", web::post().to(receive_block))
    })
    .bind(bind_addr)?
    .run()
    .await
}
